package lumis.portal.webfilereplication;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import lumis.portal.event.persistence.PortalEventEntityListener;
import lumis.portal.filesystem.PortalFile;
import lumis.portal.manager.ManagerFactory;

import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.NamedQueries;
import org.hibernate.annotations.NamedQuery;

/**
 * Configuration of a replication of web files.
 * 
 * @version $Revision: 13093 $ $Date: 2011-05-28 18:40:18 -0300 (Sat, 28 May 2011) $
 * @since 6.0.0
 */
@SuppressWarnings("serial")
@Entity
@Table(name = "lum_WFReplication")
@GenericGenerator(name = "portal-guid", strategy = "lumis.portal.dao.hibernate.GuidGenerator")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@EntityListeners(value = PortalEventEntityListener.class)
@NamedQueries(value=
{
	@NamedQuery(name = WebFileReplication.NAMED_QUERY_FIND_WEB_FILE_REPLICATION, 
			query = "from WebFileReplication",
			cacheable = true, cacheRegion = "lumis.portal.url.WebFileReplication#query")
})
public class WebFileReplication implements Serializable
{
	/**
	 * Named query that returns all the persisted web file replications.
	 * @since 6.0.0
	 */
	static final String NAMED_QUERY_FIND_WEB_FILE_REPLICATION = "findWebFileReplication";

	@Id
	@GeneratedValue(generator = "portal-guid")
	private String id;

	private String name;

	@ManyToOne(fetch = FetchType.EAGER)
	@Fetch(FetchMode.JOIN)
	@JoinColumn(name = "type", nullable = false)
	private WebFileReplicationType type;

	private String filterChannelId;
		
	private String filterPath;
		
	@OneToMany(cascade = CascadeType.ALL, mappedBy="replication", fetch = FetchType.EAGER)
	@BatchSize(size=500)
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	private Collection<WebFileReplicationPropertyValue> propertyValues = new ArrayList<WebFileReplicationPropertyValue>();

	/**
	 * Replicates a file to a repository.
	 * 
	 * @param file
	 *            The file to be replicated.
	 * @throws FileReplicationException
	 *             If any error occurs replicating the file.
	 * @since 6.0.0
	 */
	void replicateWrite(PortalFile file) throws FileReplicationException
	{
		IFileReplicator fileReplicator = getFileReplicator();

		fileReplicator.initialize();
		try
		{
			if (filtersMatch(file))
			{
				fileReplicator.write(file);
			}
		}
		finally
		{
			fileReplicator.dispose();
		}
	}

	/**
	 * Removes a file from the repository.
	 * 
	 * @param file
	 *            The file to be removed.
	 * @throws FileReplicationException
	 *             If any error occurs removing the file.
	 * @since 6.0.0
	 */
	void replicateDeletion(PortalFile file) throws FileReplicationException
	{
		IFileReplicator fileReplicator = getFileReplicator();

		fileReplicator.initialize();
		try
		{
			if (filtersMatch(file))
			{
				fileReplicator.delete(file);
			}
		}
		finally
		{
			fileReplicator.dispose();
		}
	}
	
	/**
	 * Verifies if the filters match the file to be replicated.
	 * @param file	an instance of PortalFile.
	 * @return	true if the filters match, otherwise returns false.
	 * @throws FileReplicationException if an error occurs calling the filters.
	 * @since 6.0.0
	 */
	boolean filtersMatch(PortalFile file) throws FileReplicationException
	{
		return ((FilterChannelHelper.matches(file, getFilterChannelId()))
				&& (FilterPathHelper.matches(file, getFilterPath())));
	}

	/**
	 * Returns a file replicator according to this replication type.
	 * 
	 * @return the file replicator.
	 * @throws FileReplicationException
	 *             If any error occurs retrieving the implementation.
	 * @since 6.0.0
	 */
	private IFileReplicator getFileReplicator() throws FileReplicationException
	{
		try
		{
			String className = this.getType().getFileReplicatorClassName();
			Class<?> clazz = ManagerFactory.getDeploymentManager().getClassLoader().loadClass(className);
			IFileReplicator fileReplicator = (IFileReplicator) clazz.newInstance();
			fileReplicator.setWebFileReplication(this);

			return fileReplicator;
		}
		catch (Exception e)
		{
			throw new FileReplicationException(e);
		}
	}

	/**
	 * Returns the identifier of the replication.
	 * 
	 * @return the identifier of the replication.
	 * @since 6.0.0
	 */
	public String getId()
	{
		return id;
	}

	/**
	 * Returns the name of the replication.
	 * 
	 * @return the name of the replication.
	 * @since 6.0.0
	 */
	public String getName()
	{
		return name;
	}

	/**
	 * Defines the name of the replication.
	 * 
	 * @param name
	 *            the name to be defined.
	 * @since 6.0.0
	 */
	public void setName(String name)
	{
		this.name = name;
	}

	/**
	 * Returns the type of the replication.
	 * 
	 * @return the type of the replication.
	 * @since 6.0.0
	 */
	public WebFileReplicationType getType()
	{
		return type;
	}

	/**
	 * Defines the type of the replication.
	 * 
	 * @param type
	 *            the type to be defined.
	 * @since 6.0.0
	 */
	public void setType(WebFileReplicationType type)
	{
		this.type = type;
	}
		
	/**
	 * Returns the filter channel id.
	 * @return	the filter channel id.
	 */
	public String getFilterChannelId()
	{
		return filterChannelId;
	}

	/**
	 * Defines the filter channel id.
	 * @param filterChannelId	the filter channel id.
	 */
	public void setFilterChannelId(String filterChannelId)
	{
		this.filterChannelId = filterChannelId;
	}

	/**
	 * Returns the path filter.
	 * @return	the string value to the filter path.
	 */
	public String getFilterPath()
	{
		return filterPath;
	}

	/**
	 * Defines the path filter.
	 * @param filterPath	the string value to path filter.
	 */
	public void setFilterPath(String filterPath)
	{
		this.filterPath = filterPath;
	}

	/**
	 * Returns the properties values of the replication.
	 * 
	 * @return the properties values of the replication.
	 * @since 6.0.0
	 */
	public Collection<WebFileReplicationPropertyValue> getPropertyValues()
	{
		return propertyValues;
	}

	/**
	 * Defines the the properties values of the replication.
	 * 
	 * @param propertyValues
	 *            the properties values to be defined.
	 * @since 6.0.0
	 */
	public void setPropertyValues(Collection<WebFileReplicationPropertyValue> propertyValues)
	{
		for (WebFileReplicationPropertyValue oldValue: this.propertyValues)
			ManagerFactory.getEntityManager().remove(oldValue);
		this.propertyValues.clear();
		this.propertyValues.addAll(propertyValues);
	}
}
